// TextureSynthesizer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Utils.h"

#include "TextureSynthesizer.h"

using namespace System::Windows::Forms;

namespace TextureSynthesizer {

	// Global variables
	vector<TextonGroup*> g_clusters;
	IplImage* g_originalImage;


/**
  This function compares detector with a block at specific offset within
  the inputImage calculates the difference between them and returns the 
  square of it
*/
int CalculateDifferenceGray(IplImage* detectorImage, IplImage* inputImage, int xOffset, int yOffset)
{
	int sum = -1;
	// Go over all pixels in the detector
	for(int x=0; x < detectorImage->width; x++) {
		for(int y=0; y < detectorImage->height; y++) {

			// Get pixel value from the input image
			int inputVal = (int)(cvGet2D(inputImage,yOffset + y, xOffset + x).val[0]);
			// Get pixel value from detector
			int detectorVal = (int)(cvGet2D(detectorImage,y,x).val[0]);

			int diff = inputVal-detectorVal;
			int sqDiff = POW2(diff);
			sum += sqDiff;
		}
	}

	return sum;
}

int CalculateDifferenceRGB(IplImage* detectorImage, IplImage* inputImage, int xOffset, int yOffset)
{
	int sum = -1;
	// Go over all pixels in the detector
	for(int x=0; x < detectorImage->width; x++) {
		for(int y=0; y < detectorImage->height; y++) {

			// Get pixel value from the input image
			int B = (int)(cvGet2D(inputImage,yOffset + y, xOffset + x).val[0]);
			int G = (int)(cvGet2D(inputImage,yOffset + y, xOffset + x).val[1]);
			int R = (int)(cvGet2D(inputImage,yOffset + y, xOffset + x).val[2]);

			// Get pixel value from detector
			int B1 = (int)(cvGet2D(detectorImage,y,x).val[0]);
			int G1 = (int)(cvGet2D(detectorImage,y,x).val[1]);
			int R1 = (int)(cvGet2D(detectorImage,y,x).val[2]);

			int diffB = (B - B1);
			int diffG = (G - G1);
			int diffR = (R - R1);
			int sqDiff = POW2(diffB) + POW2(diffG) + POW2(diffR);
			sum += sqDiff;
		}
	}

	return sum;
}

/**
  Loads specified number of texton detectors from the folder
  For simplicity assumes detectors are called t0.bmp,..,tn.bmp
  Also creates the inverts of the detectors
*/
void LoadTextonDetectors(IplImage** detectors, int startIdx, char* strFolderPath, int numDetectors)
{
	//Grab texton detectors from BMP files [t0.bmp, t1.bmp, ..., t7.bmp]
		
	char str[MAX_PATH] = {0};
	for(int i=0; i < numDetectors; i++) {
		std::string strDetectorPath = strFolderPath;
		sprintf_s(str,MAX_PATH,"\\t%d.bmp\0",(i+1));
		strDetectorPath += str;

		printf("Loading detector: %s\n", strDetectorPath.c_str());

		detectors[2*i + startIdx] = cvLoadImage(strDetectorPath.c_str(),CV_LOAD_IMAGE_GRAYSCALE);
		//~~~~~~~~~~~ perhaps smooth the texton detectors? ~~~~~~~~~~~
		//cvSmooth(detectors[2*i],detectors[2*i]);

		//~~~~~~~~~~ unmark to show texton detector ~~~~~~~~~~~
		//cvShowImage("input",detectors[2*i]);
		//cvWaitKey();

		//Create the "invert" texton detector
		detectors[2*i + 1 + startIdx] = cvCloneImage(detectors[2*i]);
		cvSet(detectors[2*i + 1 + startIdx],cvScalarAll(255));
		cvSub(detectors[2*i + 1 + startIdx],
			detectors[2*i + startIdx],
			detectors[2*i + 1 + startIdx]);

		//~~~~~~~~~~ unmark to show invert texton detector ~~~~~~~~~~~
		//cvShowImage("input",detectors[2*i + 1]);
		//cvWaitKey();
	}
}

/**
 * Extracts all possible detectors with specified width and height 
 * from the supplied image
 */
IplImage** ExtractDetectorsFromImage(IplImage* image, int detectorWidth, int detectorHeight)
{
	int imageWidth = image->width;
	int imageHeight = image->height;

	// Calculate number of detectors in the image
	int numDetectors = ((imageWidth - detectorWidth) * (imageHeight - detectorHeight));

	IplImage** detectors = new IplImage*[numDetectors];

	// Scan the image and extract all possible detectors
	for(int x = 0; x < (imageWidth - detectorWidth - 1); x++) {
		for(int y = 0; y < (imageHeight - detectorHeight - 1); y++) {

			CvRect rect = cvRect(x,y,detectorWidth,detectorHeight);
			CvMat* tplate = cvCreateMat(detectorWidth, detectorHeight, CV_8UC1);
            cvGetSubRect(image, tplate, rect );
		
			IplImage* textonImage = cvCreateImage(cvSize(detectorWidth, detectorHeight),
                image->depth,
                image->nChannels);

			textonImage = cvGetImage(tplate, textonImage);
			detectors[x * imageWidth + y] = textonImage;

			IplImage* texBig = cvCreateImage(
			cvSize(textonImage->width * 5, textonImage->height * 5),
			textonImage->depth,
			textonImage->nChannels);
	    	cvResize(textonImage,texBig);
		    cvShowImage("texton",texBig);	
			cvWaitKey();

		}
	}

	return detectors;
}

/**
 * Use k means algorithm to cluster textons. Returned an array of texton clusters
 */
vector<vector<Texton*> > ClusterTextons(vector<Texton*> vecTextons)
{
  vector<vector<Texton*> > clusters;
  return clusters;
}



static int gId = 0;

/**
 * Combine all overlapping textons into one texton
 * @param - vecTextons - input vector of textons
 * @return - vector of texton gropups
 */ 
vector<TextonGroup> GroupOverlappingTextons(const vector<Texton*>& _vecTextons, IplImage* orig)
{
    vector<TextonGroup> vecResult;

	vector<Texton*> vecTextons = _vecTextons;


	for(vector<Texton*>::const_iterator itr = vecTextons.begin(); itr != vecTextons.end(); itr++ ) {
		Texton* texton = *itr;
		// Create a new group and add the texton to it
		TextonGroup textonGroup;
		textonGroup.detectorId = texton->detectorId;
		textonGroup.levelFound = texton->levelFound;
		textonGroup.textonId = gId++;
		textonGroup.AddTexton(texton);

		// Check every other texton in the group
		for(vector<Texton*>::iterator itr2 = vecTextons.begin(); itr2 != vecTextons.end(); ) {
		  // Do not compare the texton to itself
		  if (itr != itr2) {
		    Texton* texton2 = *itr2;
			  // If any of the members of the current group intersect with the texton, add it to the group
			  if (textonGroup.Intersects(texton2)) {
				  // Add texton to the group, and remove it from the vector
				  textonGroup.AddTexton(texton2);
				  itr2 = vecTextons.erase(itr2);
			  }
			  else
			  {
				  itr2++;
			  }
		  }
		  else 
		  {
			  itr2++;
		  }
		} // for

		//if (textonGroup.Size() > 1) {
			textonGroup.UpdateImage(orig);
			vecResult.push_back(textonGroup);
			textonGroup.Save(OUTPUT_FOLDER);
		//}
		
	}

	return vecResult;
}

/**
 * Calculate the median distance between the provided image
 * and detector applied to each point in the image
 */
int CalculateMedian(IplImage* image, IplImage* detector)
{
	// Used for naive media calculation
	std::vector<int> sums;

	//scan image on specific pyramid level for texton
	for(int x = 0; x < (image->width - detector->width - 1); x++) {
		for(int y = 0; y < (image->height - detector->height - 1); y++) {
			int sum = CalculateDifferenceGray(detector, image, x,y);
			sums.push_back(sum);
		}
	}

	sort(sums.begin(), sums.end());
	return *(sums.begin()+sums.size()/2);
}

void MakeGaborFilters(IplImage** gabors, int& count) {
	double Sigma = PI/4;
	double F = sqrt(2.0);
	//char str[20];
	for(int i = 0; i < 8; i++) {
		CvGabor *gabor1 = new CvGabor;
		gabor1->Init(PI/8 * i, 2, Sigma, F);

		gabors[i] = cvCreateImage( cvSize(gabor1->get_mask_width(), gabor1->get_mask_width()), IPL_DEPTH_8U, 1);
		IplImage* temp = gabor1->get_image(CV_GABOR_REAL);
		cvCopy(temp,gabors[i]);
		cvAdd(gabors[i],gabors[i],gabors[i]);
	}

	IplImage* i = cvCreateImage(cvSize(gabors[0]->width,gabors[0]->height),8,1);
	cvZero(i);
//	cvAddWeighted(gabors[0],0.125,gabors[1],0.125,0.0,i);
	for(int itr=0;itr<8;itr++) {
		cvAddWeighted(i,1.0,gabors[itr],0.125,0.0,i);
	}
	IplImage* ones = cvCreateImage(cvSize(gabors[0]->width,gabors[0]->height),8,1);
	cvZero(ones);
	gabors[8] = i;
	
	i = cvCreateImage(cvSize(gabors[0]->width,gabors[0]->height),8,1);
	cvCopy(gabors[8],i);

	cvZero(ones);
	cvAddS(ones,cvScalarAll(255),ones);
	cvSub(ones,i,i);
	gabors[9] = i;

	cvReleaseImage(&ones);

	count += 10;
}

CvMat* CreateHistograms(vector<Texton* >& hits, IplImage* RGBimage, bool use_intensity)
{
	int kmeansVecLength = 30;
	if(use_intensity) kmeansVecLength = 40;

	CvMat* histogramsVectorsArray = cvCreateMat((int)(hits.size()),kmeansVecLength,CV_32FC1);

	int texCount = 0;

	IplImage* mask = cvCreateImage(cvGetSize(RGBimage),8,1);

	int bins = 10;
	//IplImage* histImg = cvCreateImage(cvSize(bins*20*3,200),8,3);

    IplImage* r_plane = cvCreateImage( cvGetSize(RGBimage), 8, 1 );
    IplImage* g_plane = cvCreateImage( cvGetSize(RGBimage), 8, 1 );
    IplImage* b_plane = cvCreateImage( cvGetSize(RGBimage), 8, 1 );
    IplImage* intensity = cvCreateImage( cvGetSize(RGBimage), 8, 1 );

	cvCvtPixToPlane( RGBimage, r_plane, g_plane, b_plane, 0 );

	cvCvtColor(RGBimage,intensity,CV_RGB2GRAY);

	//cvNamedWindow("red");
	//cvShowImage("red",r_plane);
	//cvNamedWindow("green");
	//cvShowImage("green",g_plane);
	//cvNamedWindow("blue");
	//cvShowImage("blue",b_plane);
	//cvWaitKey(0);

	int num_planes = 3;
	IplImage* planes[] = { r_plane , g_plane, b_plane, intensity };
	if(use_intensity) {
		num_planes = 4;
	}

	int hist_size[] = { bins };//, g_bins, b_bins};
    float r_ranges[] = { 0, 255 };
	float* ranges[] = { r_ranges};

    CvHistogram* hist;
	
	// Create one dimensional histogram of size 10 (number of bins)
	hist = cvCreateHist( 1, hist_size, CV_HIST_ARRAY, ranges, 1 );
	
	//cvSet(histImg,cvScalarAll(255));
	//CvScalar cols[] = {CV_RGB(255,0,0),CV_RGB(0,255,0),CV_RGB(0,0,255)};

	for(vector<Texton*>::iterator it = hits.begin(); it != hits.end(); it++ ) 
	{
		Texton* t = *it;
		Texton tt = *(t);
		//Texton* ttt = &tt;

		//if(t->levelFound > 0) {
		//	double lvlScale = pow((1 / PYRAMID_SCALE), t->levelFound);
		//	tt.Scale(lvlScale);
		//}

		// Set mask to the location of specific texton
		cvZero(mask);
		cvRectangle(mask, tt.TopLeft(), tt.BottomRight(), CV_RGB(255,255,255),CV_FILLED);

		int cnt = 0;
		for(int color = 0; color < num_planes; color++) 
		{
			//IplImage* p[] = {planes[color]};

			// Reset previous historgram
			cvClearHist(hist); // ALEX? Not needed if 3rd parameter (accumulate is set to false?)
			cvCalcHist(&planes[color] ,hist, 0, mask );

			// ALEX? Unused, temporarily commented out
		    //float max_value = 0;	
			//cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );

			for(int c = 0 ; c < bins ; c++) 
			{
				float bin_val = cvQueryHistValue_1D( hist, c );
				histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = (int)(bin_val);
			}
		}

		//clear mask
		//cvRectangle(mask, tt.TopLeft(), tt.BottomRight(), CV_RGB(0,0,0),CV_FILLED);

		//more data to the k-means
//		histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->levelFound;
//		histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->detectorId;
		//histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->x;
		//histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->y;
		//histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->x;
		//histogramsVectorsArray->data.i[texCount * kmeansVecLength + cnt++] = ttt->y;

		//cvShowImage("histogram",histImg);
		//cvWaitKey(10);

		//for(int i=0;i<kmeansVecLength;i++)
		//{
		//  printf("%d, ",histogramsVectorsArray->data.i[texCount * kmeansVecLength + i]);
		//}
		//printf("\n");
		texCount++;
	}

	// Clean up
	cvReleaseImage(&mask);
	
	//cvReleaseImage(&histImg);

	return histogramsVectorsArray;
}

void CreateBarsWindow(int* clusters_num_p, int* dilates_nums) {
	cvNamedWindow("bars");
	cvResizeWindow("bars",300,50 + (*clusters_num_p * 50));
	cvCreateTrackbar("clusters","bars",clusters_num_p,MAX_NUM_OF_CLUSTERS,NULL);
	for(int i=0;i<*clusters_num_p;i++) {
		char str[25] = {0}; sprintf_s(str,25,"dilates %d",i);
		cvCreateTrackbar(str,"bars",&(dilates_nums[i]),10,NULL);
	}
}

void GroupTextonsUsingHistograms(vector<Texton* >& hits, 
								 IplImage* RGBimage, 
								 vector<TextonGroup*>& groups,
								 int surroundingHistPad,
								 int* num_clusters,
								 bool use_intensity) 
{	
	CvMat* histogramsVectorsArray = CreateHistograms(hits, RGBimage,use_intensity);

	// Color constants for marking different textons
	CvScalar color_tab[] = {
		CV_RGB(255,0,0),
		CV_RGB(0,255,0),
		CV_RGB(0,0,255),
		CV_RGB(255,100,0),
		CV_RGB(0,100,255),
		CV_RGB(100,255,0),
		CV_RGB(100,0,255),
		CV_RGB(255,0,100),
		CV_RGB(100,100,255),
		CV_RGB(100,255,100),
		CV_RGB(255,100,100),
		CV_RGB(255,255,0)
	};

	CvScalar color_tab_shadow[] = {
		cvScalar(155,0,0,50),
		cvScalar(0,155,0,50),
		cvScalar(0,0,155,50),
		cvScalar(155,50,0,50),
		cvScalar(0,50,155,50),
		cvScalar(50,155,0,50),
		cvScalar(50,0,155,50),
		cvScalar(155,0,50,50),
		cvScalar(50,50,155,50),
		cvScalar(50,155,50,50),
		cvScalar(155,50,50,50),
		cvScalar(155,155,0,50)
	};

	IplImage* copyOfOrig = cvCreateImage(cvGetSize(RGBimage),RGBimage->depth,RGBimage->nChannels);
	IplImage* mask = cvCreateImage(cvGetSize(RGBimage),8,1);

	IplImage* clustersSpread = cvCreateImage(cvGetSize(RGBimage),8,1);

	IplImage* tmp = cvCreateImage(cvGetSize(RGBimage),8,1);

	int clusters_num = *num_clusters;
	int dilates_num[MAX_NUM_OF_CLUSTERS] = {0};

	// Create a vector of cluster indices for each of the textons
	CvMat* clusters = cvCreateMat( (int)(hits.size()), 1, CV_32SC1 );

	cvNamedWindow("clusters");

	while(true) {
		groups.clear();
		CreateBarsWindow(&clusters_num,dilates_num);

		cvZero(clusters);
		cvResetImageROI(RGBimage);
		cvZero(clustersSpread);

		// Sanity check for positive values
		if (clusters_num == 0) clusters_num = 1;
		//if (dilates_num == 0) dilates_num = 1;

		int clusterColMul = (int)(255.0 / (clusters_num + 1));

		// Group textons with similar color histograms into clusters
		// Number of clusters can be specified from the UI
		cvKMeans2( histogramsVectorsArray, clusters_num, clusters,
					   cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 20, 1.0 ));

		int windows_num = clusters_num;
		IplImage** clusterPxls = new IplImage*[windows_num];
		IplImage** clusterAlpha = new IplImage*[windows_num];

		for(int i=0;i<windows_num;i++){
			clusterPxls[i] = cvCreateImage(cvGetSize(RGBimage),8,3);
			clusterAlpha[i] = cvCreateImage(cvGetSize(RGBimage),8,1);
			//cvSet(clusterPxls[i],cvScalarAll(127.0));
			cvZero(clusterPxls[i]);
			cvZero(clusterAlpha[i]);
		}

		cvCopy(RGBimage,copyOfOrig);
		for(unsigned int i=0;i<hits.size();i++) 
		{
			// Copy current texton
			Texton tt = *(hits[i]);
		
			// Calculate texton scale and update texton coordinates
			double lvlScale = pow((1 / PYRAMID_SCALE), tt.levelFound);

			tt.Scale(lvlScale);

			int cluster = clusters->data.i[i];
			cvRectangle(copyOfOrig, tt.TopLeft(), tt.BottomRight(), color_tab[cluster],CV_FILLED);

			cvRectangle(clustersSpread, 
				tt.TopLeft(), tt.BottomRight(),
				cvScalar((cluster+1) * clusterColMul),
				CV_FILLED);

			//cvRectangle(clusterPxls[cluster],
			//	cvPoint(ttt->x,ttt->y),
			//	cvPoint(ttt->x+ttt->width,ttt->y+ttt->height),
			//	color_tab[cluster],
			//	1);

			//copy detected square from orig image to cluster pixels image
			cvZero(mask);
			cvRectangle(mask, tt.TopLeft(), tt.BottomRight(), CV_RGB(255,255,255),CV_FILLED);
			cvCopy(RGBimage,clusterPxls[cluster],mask);

			cvRectangle(clusterAlpha[cluster], tt.TopLeft(), tt.BottomRight(), CV_RGB(255,255,255),CV_FILLED);
		}

		cvShowImage("clusters",copyOfOrig);

		CvMemStorage* storage = cvCreateMemStorage();
		CvSeq* comps = NULL;

		//IplImage** clusterPxlsRGBA = new IplImage*[windows_num];
		IplImage* tmpMask = cvCreateImage(cvGetSize(RGBimage),8,1);
		IplImage* textonImg = NULL;
		IplImage* tmp = cvCreateImage(cvGetSize(tmpMask),8,1);
		int total = 0;
		cvNamedWindow("mask");
		for(int i=0;i<windows_num;i++){
			for(int dI = 0; dI < dilates_num[i]; dI++) {
				cvErode(clusterAlpha[i],clusterAlpha[i]);
			}
			CvContourScanner s = cvStartFindContours(clusterAlpha[i],storage,sizeof(CvContour),CV_RETR_CCOMP);

			CvSeq* sq = NULL;
			int c = 0;
			while((sq = cvFindNextContour(s))!=NULL) {
				CvContour* cont = (CvContour*)sq;
				cvDrawContours(clusterPxls[i],sq,color_tab[c],CV_RGB(255,0,0),0,2);
				cvResetImageROI(tmpMask);
				cvZero(tmpMask);
				cvDrawContours(tmpMask,sq,CV_RGB(255,255,255),CV_RGB(0,0,0),0,CV_FILLED);

				if(cont->h_prev) {
					int aaa = 1;
				}

				/*cvShowImage("mask",tmpMask);
				cvWaitKey(0);*/
				
				//TODO: draw out holes as well...

				CvRect r = cont->rect; // GetMaskBoundingRect(tmpMask);

				//check for a completely eroded texton
				if(r.width <= 0 || r.height <= 0) continue;

				textonImg = cvCreateImage(cvSize(r.width,r.height),8,3);
				cvZero(textonImg);
				cvSetImageROI(RGBimage,r);
				cvSetImageROI(tmpMask,r);
				cvCopy(RGBimage,textonImg,tmpMask);

				TextonGroup* g = new TextonGroup();
				cvSet(tmp,CV_RGB(255,255,255));
				cvSetImageROI(tmp,r);
				cvSub(tmp,tmpMask,tmp);
				cvResetImageROI(tmp);
				g->SetSurroundingHist(
					CalcHistogramForRect(clustersSpread,
										cvRect(r.x - surroundingHistPad,r.y - surroundingHistPad, r.width + surroundingHistPad * 2, r.height + surroundingHistPad * 2),
										windows_num,
										tmp)
					);

				g->SetImage(textonImg);
				g->SetMask(tmpMask);
				g->x = r.x;
				g->y = r.y;
				g->width = r.width;
				g->height = r.height;
				g->detectorId = i;//cluster num
				g->levelFound = i * clusterColMul;//cluster color in spread
				g->textonId = total++;
				//g->Save("../Output/");
				groups.push_back(g);

				c++;
			}
			cvEndFindContours(&s);

			// Uncomment to present each cluster in separate window
			char str[256] = {0};	sprintf(str,"cluster: %d",i);
			cvNamedWindow(str);
			cvShowImage(str,clusterPxls[i]);
		}
		cvDestroyWindow("mask");

		cvReleaseMemStorage(&storage);
		// Wait for user input
		int c = cvWaitKey(0);

		// Close all open cluster windows
		for(int i =0;i<windows_num;i++) {
			char str[256] = {0};	sprintf_s(str,256,"cluster: %d",i);
			cvDestroyWindow(str);
			cvReleaseImage(&(clusterPxls[i]));
			cvReleaseImage(&(clusterAlpha[i]));
			//cvReleaseImage(&(clusterPxlsRGBA[i]));
		}
		delete clusterPxls;
		delete clusterAlpha;
		//delete clusterPxlsRGBA;

		cvReleaseImage(&tmp);
		cvReleaseImage(&tmpMask);

		cvDestroyWindow("bars");

		// Copy the result and exit the loop
		if(c==VK_ESCAPE) {

			// TODO: Copy the resulting textons to the output 'groups' array
			break;
		}
	}//while

	//erode and yank out textons

	*num_clusters = clusters_num;

	// Cleanup
	cvReleaseMat(&histogramsVectorsArray);
	cvReleaseMat(&clusters);

	cvReleaseImage(&copyOfOrig);
	cvReleaseImage(&clustersSpread);
	cvReleaseImage(&mask);
}

CvMat* CalcHistogramForRect(IplImage* img, CvRect r, int bins, IplImage* histMask = NULL) {
	IplImage* planes[] = { img };
	int hist_size[] = { bins };//, g_bins, b_bins};
    float r_ranges[] = { 0, 255 };
	float* ranges[] = { r_ranges};
	CvMat* vector = cvCreateMat(1,bins,CV_32SC1);
    CvHistogram* hist;
	
	// Create one dimensional histogram of size <number of bins>
	hist = cvCreateHist( 1, hist_size, CV_HIST_ARRAY, ranges, 1 );
	cvClearHist(hist);

	IplImage* mask = cvCreateImage(cvGetSize(img),8,1);
	cvZero(mask);
	CvPoint ul = cvPoint((r.x >= 0) ? r.x : 0,(r.y >= 0) ? r.y : 0);
	CvPoint br = cvPoint((r.x + r.width < img->width) ? r.x + r.width : img->width - 1,
					(r.y + r.height < img->height) ? r.y + r.height : img->height - 1);
	cvRectangle(mask,ul,br,CV_RGB(255,255,255),CV_FILLED);
	if(histMask) {
		cvAnd(mask,histMask,mask);
	}
	//cvNot(mask,mask);

	//IplImage* tmp = cvCreateImage(cvGetSize(img),8,3);
	//cvCvtColor(img,tmp,CV_GRAY2RGB);
	//cvSet(tmp,CV_RGB(0,0,0),mask);
	//cvRectangle(tmp,ul,br,CV_RGB(255,0,0),2);
	//cvNamedWindow("hist area");
	//cvShowImage("hist area",tmp);
	//cvWaitKey(0);
	//cvReleaseImage(&tmp);
	//cvDestroyWindow("hist area");

	cvCalcHist(&img ,hist, 0, mask );

	int cnt = 0;
	for(int c = 0 ; c < bins ; c++) 
	{
		float bin_val = cvQueryHistValue_1D( hist, c );
		vector->data.i[cnt++] = (int)(bin_val);

		//printf("%d,",(int)bin_val);
	}
	//printf("\n");
	
	cvReleaseHist(&hist);
	cvReleaseImage(&mask);

	return vector;
}

CvRect GetMaskBoundingRect(IplImage* img) {
	CV_FUNCNAME("GetMaskBoundingRect");
	__BEGIN__;
	if(img->nChannels > 1) {
		CV_ERROR(-1,"This only works for 8bit images");
		return cvRect(0,0,0,0);
	}
	int minX,maxX,minY,maxY;
	minX = img->width;
	minY = img->height;
	maxX = maxY = 0;
	for(int i=0;i<img->height;i++) {
		int line = i*img->widthStep;
		for(int j=0;j<img->width;j++) {
			if(*(cvPtr2D(img,i,j)) > 0) {
			//if(img->imageData[line + j] > 0) {
				if(i<minY) minY = i;
				if(i>maxY) maxY = i;
				if(j<minX) minX = j;
				if(j>maxX) maxX = j;
			}
		}
	}

	return cvRect(minX,minY,maxX-minX,maxY-minY);
	__END__;
}

/**
 * This functions receives vector of textons and returns a group of textons similar to
 * the provided sample
 */
TextonGroup GroupSimilarTextons(const vector<Texton*>& vecTextons, Texton* sample)
{
	TextonGroup similarTextonGroup;

	for (unsigned int i = 0; i < vecTextons.size(); i++)
	{
		Texton* currentTexton = vecTextons[i];
		if ((currentTexton != sample) && currentTexton->IsSimilar(sample)) {
			similarTextonGroup.AddTexton(currentTexton);
		}
	}

	return similarTextonGroup;
}

void ExtractTextonsUsingGrid(System::Object^ props) {
	TextureSynthesizer::TexSynthProps^ tProps = ((TextureSynthesizer::TexSynthProps^)props);
	g_clusters.clear();

	 char filename[MAX_PATH] = {0};
	 StringToCharPointer(tProps->Filename,filename,MAX_PATH);
	 
	// Load pattern
	IplImage* originalImage = cvLoadImage(filename);

	// Create a global copy of the input image
	g_originalImage = cvCreateImage(cvGetSize(originalImage),8,3);
	cvCopy(originalImage,g_originalImage);

	IplImage* copyOfOrig = cvCreateImage(cvGetSize(originalImage),8,3);
	cvCopy(originalImage,copyOfOrig);

	if (originalImage == NULL) {
		printf("Could not load image %s\n", filename);//TODO msgbox
		return;
	}

	IplImage* gray = cvCreateImage(cvSize(originalImage->width,originalImage->height),8,1);
	cvCvtColor(originalImage,gray,CV_RGB2GRAY);

	vector<Texton*> vecTextons;
	int gridJump = tProps->GridStep;
	int boxSize = tProps->PatchSize;
	for(int x = 0; x < (gray->width - boxSize - 1); x+=gridJump) {
		for(int y = 0; y < (gray->height - boxSize - 1); y+=gridJump) {		
			IplImage* textonImage = cvCreateImage(
				cvSize(boxSize, boxSize),
                copyOfOrig->depth,
                copyOfOrig->nChannels);
			
			// Set the real image to the texton
			Texton* texton = new Texton(x,y,boxSize, boxSize, -1, 0, 0);
			texton->SetImage(textonImage);
			texton->textonId = (int)(vecTextons.size());

			// Add texton to the global list
			vecTextons.push_back(texton);

			cvDrawRect(copyOfOrig,cvPoint(x,y),cvPoint(x+boxSize,y+boxSize),CV_RGB(255,0,0));
		}
	}

	//cvNamedWindow("Input");
	//cvShowImage("input",copyOfOrig);
	//cvWaitKey(0);

	printf("Found: %d textons\n", vecTextons.size());

	int num_clusters = tProps->NumberOfClusters;
	GroupTextonsUsingHistograms(vecTextons,
		originalImage,
		g_clusters,
		tProps->SurroundingHistPad,
		&num_clusters,
		tProps->UseIntesityForHist);

	tProps->NumberOfClusters = num_clusters;

	printf("Created: %d clusters\n", g_clusters.size());


	//  Cleanup
	cvDestroyAllWindows();

	for(vector<Texton*>::iterator itr = vecTextons.begin(); itr != vecTextons.end(); itr++ ) {
		Texton* th = (*itr);
		IplImage* image = th->GetImage();
		cvResetImageROI(image);
	    //cvReleaseImage(&image);
		delete th;
	}

	cvReleaseImage(&originalImage);
	cvReleaseImage(&gray);

	
	return;
}

void ExtractTextons(System::Object^ filenameString)
{
//	System::String^ filenameStr = (System::String^)((System::Array^)a)->GetValue(0);
	g_clusters.clear();

	 char filename[MAX_PATH] = {0};
	 StringToCharPointer((System::String^)filenameString,filename,MAX_PATH);
	 

	// Load pattern
	IplImage* originalImage = cvLoadImage(filename);
	IplImage* copyOfOrig = cvCreateImage(cvGetSize(originalImage),8,3);

	// TESTING CODE, 
	//std::vector<CvScalar> colorBuckets;
	//CreatePixelBuckets(originalImage, colorBuckets);
	

	if (originalImage == NULL) {
		printf("Could not load image %s\n", filename);//TODO msgbox
		return;
	}

	// Present the grayscale of the input image
	cvNamedWindow("Input");

	IplImage* gray = cvCreateImage(cvSize(originalImage->width,originalImage->height),8,1);
	cvCvtColor(originalImage,gray,CV_RGB2GRAY);

	// Create multiscale pyramid from the equalized grayscale image
	TextureSynthesizer::MultiScalePyramid pyramid(gray, (float)(PYRAMID_SCALE), NUM_LEVELS);

	//~~~~~~~~~~~~ uncomment to see the pyramid ~~~~~~~~~~~~
	//for(int i=0;i<5;i++) {
	//	cvShowImage("input",m[i]);
	//	cvWaitKey();
	//}

	//~~~~~~~~~~~~ edge detect... maybe a good idea ~~~~~~~~~~~~
	//IplImage* gray32 = cvCreateImage(cvSize(gray->width,gray->height),32,1);
	//cvCanny(gray,gray,100,100);
	//cvShowImage("input",gray);
	//cvWaitKey();

	// Window used for presenting texton detectors
	cvNamedWindow("Detector");

	//TODO: make threshold DYNAMIC! median does not work..
	//int threshForDetecotr[] = {62,78,85,75,63,75,85,80,44,45,1/*,9*/,10,25};
	//int threshForDetecotr[15] = {100,100,100,100,100,100,100,100,75,75,1,6,4,100};

	//paintpeel
	int threshForDetecotr[15] = {60,75,75,75,60,75,75,75,40,40,8,8,2,65};

	//fossil
	//int threshForDetecotr[15] = {48,66,71,61,47,61,67,66,28,27,23,2,40};

	//light
//	int threshForDetecotr[15] = {25,25,25,25,25,25,25,25,20,20,8,4,50};

//	int threshForDetecotr[3] = {8,8,8};

	int textonHitThresholdInt = threshForDetecotr[0];
	double textonHitThreshold; // = textonHitThresholdInt * 1000;

	cvCreateTrackbar("thresh","Input",&textonHitThresholdInt,100,NULL);

	//Grab texton detectors from BMP files [t0.bmp, t1.bmp, ..., t7.bmp]
	int numDetectors = 0;
	IplImage** detectors = NULL;new IplImage*[30];

	DialogResult result = MessageBox::Show("Use Gabor filters?", "Choose filters", MessageBoxButtons::YesNo);
	if (result == DialogResult::Yes) 
	{
		detectors = new IplImage*[20];
		MakeGaborFilters(detectors,numDetectors);

		//Add "smooth" detectors
		detectors[numDetectors++] = cvCreateImage(cvSize(7,7),8,1);
		cvSet(detectors[numDetectors - 1],cvScalarAll(255.0*0.15));
		detectors[numDetectors++] = cvCreateImage(cvSize(7,7),8,1);
		cvSet(detectors[numDetectors - 1],cvScalarAll(255.0*0.25));
		detectors[numDetectors++] = cvCreateImage(cvSize(7,7),8,1);
		cvSet(detectors[numDetectors - 1],cvScalarAll(255.0*0.5));
		detectors[numDetectors++] = cvCreateImage(cvSize(7,7),8,1);
		cvSet(detectors[numDetectors - 1],cvScalarAll(255.0*0.75));
	}
	else
	{
	  detectors = new IplImage*[NUM_TEXTON_DETECTORS * 2];
	  LoadTextonDetectors(detectors,0, DETECTOR_PATH, NUM_TEXTON_DETECTORS);
	  numDetectors = NUM_TEXTON_DETECTORS;

	  cvEqualizeHist(gray,gray);
	}

	// Alternatively, grab the detectors from the picture itself
	//IplImage** detectors = ExtractDetectorsFromImage(gray, 7, 7);
	

	// an STL vector to hold all the texton hits
	vector<Texton*> vecTextons;
	set<pair<int,int> > locations;

	CvScalar imgAvg = cvAvg(gray);

	// main loop over all texton detectors
	for(int detectorIndex = 0; detectorIndex < numDetectors; detectorIndex++) {
		cvCopy(originalImage,copyOfOrig);
		int hitcount = 0;

		IplImage* detector = detectors[detectorIndex];

		// Create a big version of the detector to better show user
		IplImage* texBig = cvCreateImage(
			cvSize(detector->width * 5, detector->height * 5),
			detector->depth,
			detector->nChannels);
		cvResize(detector,texBig);
		cvShowImage("Detector",texBig);	
		
		int textonWidth = detector->width;
		int textonHeight = detector->height;

		//long k = detector->width * detector->height * 255;
		textonHitThreshold = textonHitThresholdInt * 2000;
		//normalize threshold for this texton, divide by avg color
		CvScalar s = cvAvg(detector);
		//double r = ();
		double normTextonHitThreshold = textonHitThreshold / s.val[0] * imgAvg.val[0];

		// FIRST ITERATION get good textons

		//IplImage* conv = cvCreateImage(cvGetSize(gray),8,1);
		//cvZero(conv);
		//CvMat* mat = cvCreateMat(7,7,CV_32F);
		////cvCopy(detector,mat);
		//double d = cvSum(detector).val[0];
		//cvConvertScale(detector,mat,1.0/d);
		//cvFilter2D(gray,conv,mat);
		//cvShowImage("Input",conv);
		//cvWaitKey(50);
		//cvReleaseImage(&conv);

		// loop pyramid level
		for(int level = FIRST_LEVEL; level < NUM_LEVELS; level++) {

			int levelWidth = pyramid[level]->width;
		    int levelHeight = pyramid[level]->height;

			//scan image on specific pyramid level for texton
			for(int x = 0; x < (levelWidth - textonWidth - 1); x++) {
				for(int y = 0; y < (levelHeight - textonHeight - 1); y++) {

					// Calculate SSD between pattern and texton detector
					//TODO: There's a faster way to do this with OpenCV... can't remember
					
					// Go over the detector and calculate the difference between it and 
					// the actual image. The difference may range from 0 (perfect match)
					// to ...?
					int sum = CalculateDifferenceGray(detector, pyramid[level], x,y);

					//~~~~~~~~~~~~ unmark for a torrent of SSDs ~~~~~~~~~~~~
					printf("Applied texton: %d, level = %d, result = %d, threshold = %d\n", detectorIndex, level, sum, normTextonHitThreshold);

					if(sum >= 0 && sum < normTextonHitThreshold) {
						//"good" texon
						pair<int, int> p(x,y);
						if(locations.find(p) != locations.end()) {
							//already have a patch in this location
							continue;
						} else {
							locations.insert(p);
						}

						Texton* texton = new Texton(x,y,textonWidth, textonHeight, detectorIndex, level, sum);

						// Calculate real texton size as function of detector and pyramid level
						int realWidth = (int)(detector->width * pow((1 / PYRAMID_SCALE), level));
						int realHeight = (int)(detector->height * pow((1 / PYRAMID_SCALE), level));

						//TODO: Think maybe x and y coordinates should be changed too

						CvRect rect = cvRect(x,y,realWidth,realHeight);
						CvMat* tplate = cvCreateMat(realWidth, realHeight, CV_8UC1);
                        cvGetSubRect(originalImage, tplate, rect );
					
						IplImage* textonImage = cvCreateImage(
							cvSize(realWidth, realHeight),
			                detector->depth,
			                detector->nChannels);
						
						// Set the real image to the texton
						texton->SetImage(cvGetImage(tplate, textonImage));

						// TODO: Think of a better way to distinguish between textons
						texton->textonId = (int)(vecTextons.size());

						// Add texton to the global list
						vecTextons.push_back(texton);

						Texton tt = *(texton);
						Texton* ttt = &tt;
						double lvlScale = pow((1 / PYRAMID_SCALE), texton->levelFound);
						tt.x = (int)(tt.x * lvlScale);
						tt.y = (int)(tt.y * lvlScale);
						tt.width = (int)(tt.width * lvlScale);
						tt.height = (int)(tt.height * lvlScale);

						cvDrawRect(
							copyOfOrig,
							cvPoint(ttt->x,ttt->y),
							cvPoint(ttt->x+ttt->width,ttt->y+ttt->height),
							CV_RGB(255,0,0),
							1);

						hitcount++;

						// Save texton
						//texton->Save(OUTPUT_FOLDER);
							   
						// UNCOMMENT THIS TO VIEW EACH INDIVIDUAL TEXTON
						/*
						cvNamedWindow("Real Texton");
		                cvShowImage("Real Texton",textonImage);
						cvWaitKey();
						*/
						
					}
				}
			}


			cvResetImageROI(pyramid[level]);

		}//end levels loop

		char str[256] = {0};
		printf("Texton index: %d, threshold: %d, hitcount: %d\n",detectorIndex, (int)normTextonHitThreshold, hitcount);
		sprintf(str,"%d (%d)\0",(int)normTextonHitThreshold,hitcount);
		cvPutText(copyOfOrig,str,cvPoint(5,15),&(cvFont(1.3,2)),CV_RGB(0,0,255));
		cvShowImage("Input",copyOfOrig);
		cvWaitKey();

		int c = cvWaitKey(30);
		if(c == VK_ESCAPE) {
			detectorIndex--;	//repeat last detector
		} else {
			textonHitThresholdInt = threshForDetecotr[detectorIndex+1];
			cvSetTrackbarPos("thresh","Input",textonHitThresholdInt);
		}

		// TODO: Try the same with textons from different detectors
	
		cvReleaseImage(&texBig);
	}// end textons loop

	cvDestroyAllWindows();

	printf("Found: %d textons\n", vecTextons.size());

    
	//GroupTextonsUsingHistograms(vecTextons,originalImage,g_clusters);

	printf("Created: %d clusters\n", g_clusters.size());


	//  Cleanup
	cvDestroyAllWindows();

	for(vector<Texton*>::iterator itr = vecTextons.begin(); itr != vecTextons.end(); itr++ ) {
		Texton* th = (*itr);
		IplImage* image = th->GetImage();
		cvResetImageROI(image);
	    //cvReleaseImage(&image);
		delete th;
	}


	// release resources
	for(int i=0; i < numDetectors; i++) {
			cvReleaseImage(&detectors[i]);
		}
	delete detectors;
	cvReleaseImage(&originalImage);
	cvReleaseImage(&gray);

	
	return;
}

void PutTexton(IplImage* output, IplImage* outputAlpha, TextonGroup* texton, int x, int y)
{
	if (!output || !texton) {
		printf("unable to put texton, parameters are NULL\n");
		return;
	}

	//paint texton pixels
	int fixX = 0; 
	if(x + texton->width > output->width) 
		fixX = (x + texton->width) - output->width;

	int fixY = 0;
	if(y + texton->height > output->height) 
		fixY = (y + texton->height) - output->height; 


	// Adjust texton size to fit the target bounds
	int g_w = texton->width - fixX;
	int g_h = texton->height - fixY;

	cvSetImageROI(output,cvRect(x,y,g_w,g_h));
	if (outputAlpha != NULL)
		cvSetImageROI(outputAlpha,cvRect(x,y,g_w,g_h));

	// Update the texton ROI to match the target bounds
	cvSetImageROI(texton->GetImage(),cvRect(0,0,g_w,g_h));
	cvSetImageROI(texton->GetMask(),cvRect(0,0,g_w,g_h));
	
	// Create temporary mask
	IplImage* tmpMask = cvCreateImage(cvSize(g_w,g_h),8,1);

	// Apply texton mask to alpha image
	if (outputAlpha != NULL) {
		cvSub(texton->GetMask(),outputAlpha,tmpMask);
		cvCopy(texton->GetMask(),outputAlpha,tmpMask);
		cvResetImageROI(outputAlpha);
	}

	// Apply texton
	cvCopy(texton->GetImage(),output,tmpMask);
	
	// Reset ROIs
	cvResetImageROI(texton->GetImage());
	cvResetImageROI(texton->GetMask());
	cvResetImageROI(output);

	cvReleaseImage(&tmpMask);

}

bool CanCover(IplImage* target, TextonGroup* texton, int x, int y)
{
	// If the target is zero, return true
	cvSetImageROI(target,cvRect(x, y, x + texton->width, y + texton->height));

	CvMat* m = cvCreateMat(texton->height, texton->width, CV_32SC1);
	cvConvert(target, m);

	CvScalar s = cvSum(m);
	if (s.val[0] == 0) {
		cvReleaseMat(&m);
		return true;
	}

	cvReleaseMat(&m);

	// Otherwise try to see whether the match is high between the applied texton
	// and the underlying target

	IplImage* region = cvCreateImage(cvSize(texton->width, texton->height),8,1);
	cvZero(region);
	cvCvtColor(target,region,CV_RGB2GRAY);

	IplImage* grayTexton = cvCreateImage(cvSize(texton->width, texton->height),8,1);
	cvZero(region);
	cvCvtColor(texton->GetImage(),grayTexton,CV_RGB2GRAY);

	double matchResult = cvMatchShapes(region, grayTexton, 1);

	bool canMatch;
	if (matchResult > 10) // check this value
		canMatch =  true;
	else
		canMatch  = false;

	printf("Match result: %f", matchResult);

	cvReleaseImage(&region);
	cvReleaseImage(&grayTexton);

	return canMatch;
}

// Extract specified rectangular region from the image
IplImage* GetRegion(IplImage* image, CvRect rect)
{
	CvMat* regionMatrix = cvCreateMat(rect.width, rect.height, CV_32SC3);
    cvGetSubRect(image, regionMatrix, rect );

	IplImage* region = cvCreateImage(cvSize(rect.width, rect.height), image->depth, image->nChannels);
	
	// Set the real image to the texton
	region = cvGetImage(regionMatrix, region);

	return region;
}



// Calculate distance between the texton and the underlying output region
long CalculateDistance(IplImage* output, TextonGroup* texton, int x, int y)
{
	printf("Calculating difference...\n");

	printf("Num channels = %d\n", output->nChannels);

	//paint texton pixels
	int fixX = 0; 
	if(x + texton->width > output->width) 
		fixX = (x + texton->width) - output->width;

	int fixY = 0;
	if(y + texton->height > output->height) 
		fixY = (y + texton->height) - output->height; 


	// Adjust texton size to fit the target bounds
	int g_w = texton->width - fixX;
	int g_h = texton->height - fixY;

	//CvMat *dist  = cvCreateMat( g_h, g_w, CV_32SC3 );

	int depth = output->depth;
	int channels = output->nChannels;

	CvSize size = cvSize(g_w, g_h);

	// Create image for each color 
	IplImage *img1B = cvCreateImage( size, depth, 1 );
	IplImage *img1G = cvCreateImage( size, depth, 1 );
	IplImage *img1R = cvCreateImage( size, depth, 1 );
	
	IplImage *img2B = cvCreateImage( size, depth, 1 );
	IplImage *img2G = cvCreateImage( size, depth, 1 );
	IplImage *img2R = cvCreateImage( size, depth, 1 );

	IplImage *diff  = cvCreateImage( size, depth, 1 );
	IplImage *dist  = cvCreateImage( size, depth, 1 );

	IplImage* region = GetRegion(output, cvRect(x, y, g_w, g_h));

	printf("Region: channels: %d, width: %d, height: %d\n", region->nChannels, region->width, region->height);

	ShowImage(region);

	cvSplit( region, img1B, img1G, img1R, NULL);
	cvSplit( texton->GetImage(), img2B, img2G, img2R, NULL );

	// Calculate difference
	cvSub( img1B, img2B, diff );
	
	cvMul( diff, diff, dist );
	cvSub( img1G, img2G, diff );
	cvMul( diff, diff, diff);
	cvAdd( diff, dist, dist );
	cvSub( img1R, img2R, diff );
	cvMul( diff, diff, diff );
	cvAdd( diff, dist, dist );

	// Cleanup, 
	cvReleaseImage( &img1B );
	cvReleaseImage( &img1G );
	cvReleaseImage( &img1R );
	cvReleaseImage( &img2B );
	cvReleaseImage( &img2G );
	cvReleaseImage( &img2R );
	cvReleaseImage( &diff );

	CvScalar result = cvSum(dist);

	printf("CalculateDistance, r0 = %f, r1 = %f, r2 = %f, r3 = %f\n", result.val[0], result.val[1], result.val[2], result.val[3]);

	cvReleaseImage( &dist );
	cvReleaseImage( &diff );
	cvResetImageROI(output);

	return (long)(result.val[0]+ result.val[1] + result.val[2] + result.val[3]);

	
}

// Checks whether the rectangle is blank
bool IsBlank(IplImage* targetImage, CvRect rect, int targetScore)
{
	cvResetImageROI(targetImage);
	CvMat* regionMatrix = cvCreateMat(rect.height, rect.width, CV_32SC3);

	regionMatrix = cvGetSubRect(targetImage, regionMatrix, rect);

	CvScalar result = cvSum(regionMatrix);

	printf("IsBlank, r0 = %f, r1 = %f, r2 = %f, r3 = %f\n", result.val[0], result.val[1], result.val[2], result.val[3]);

	if ((result.val[0] == 0) && (result.val[1] == 0) && (result.val[2] == 0) && (result.val[3] == 0))
		return true;

	return false;
}

void ShowImage(IplImage* image)
{
	cvNamedWindow("image");
	cvShowImage("image",image);
	cvWaitKey();
	cvDestroyWindow("image");
}

typedef struct TextonGroupArea {
	int area;
	int index;
} TGAREA;

bool operator < (const TGAREA& left, const TGAREA& right) { 
	return (left.area < right.area);
} 

void CreateNewTexture(System::Object^ o)
{
	if(g_clusters.size() == 0) return;

	TextureSynthesizer::TexSynthProps^ props = (TextureSynthesizer::TexSynthProps^)o;

	System::Drawing::Size s = System::Drawing::Size(props->TextureWidth,props->TextureHeight);

	IplImage* output = cvCreateImage(cvSize(s.Width,s.Height),8,3);
	cvZero(output);
	IplImage* outputAlpha = cvCreateImage(cvGetSize(output),8,1);
	cvZero(outputAlpha);
	IplImage* outputClusters = cvCreateImage(cvGetSize(output),8,1);
	cvZero(outputClusters);

	srand((int)(time(NULL)));

	cvDestroyAllWindows();

	cvNamedWindow("texture");
	cvNamedWindow("texture alpha");

	long allWhite = 255 * s.Width * s.Height;

	int* used = new int[g_clusters.size()];
	int r = 0;
	bool done = false;

	//Sort textons by their size
	vector<TGAREA > gs;
	for(unsigned int i=0;i<g_clusters.size();i++) {
		TextonGroup* tg = g_clusters[i];
		TGAREA a;
		a.area = tg->width*tg->height;
		a.index = i;
		gs.push_back(a);	//rely on shallow copy of the struct
	}
	sort(gs.begin(),gs.end());
	double halfNumTex = gs.size() / 2;
	TextonGroup* g = NULL;

	int scanStep = 32; //outputAlpha->width / 16;
	//double logSize = log((double)outputAlpha->width);
	//double floorLog = floor(logSize);
	//scanStep = (int)pow(2.0,floorLog);

	while(!done) {
		int c = cvWaitKey(1);
		if(c == VK_ESCAPE) break;
		if(scanStep==0) break;
		r++;

		if(r<props->TimesToUseModelOne) {
			props->ModelType = TexSynthProps::eMODELTYPE_ENUM::MODELTYPE_USE_BALOON_TO_SELECT_TEXTON;
		} else {
			props->ModelType = TexSynthProps::eMODELTYPE_ENUM::MODELTYPE_USE_HIST_TO_FIND_LOCATION;
		}

		CvPoint pt;
		if(props->ModelType == TexSynthProps::eMODELTYPE_ENUM::MODELTYPE_USE_HIST_TO_FIND_LOCATION) {
			// Select random texton
			double rndNum = randn_trig(halfNumTex,props->NormalDistSigma);
			unsigned int texIdx = (rndNum < 0.0) ? 0 : (unsigned int)rndNum;
			if(texIdx >= gs.size()) texIdx = gs.size()-1;
			TGAREA a = gs[texIdx];
			g = g_clusters[a.index];

			int p = props->SurroundingHistPad;
			double long min = MAXLONG64;
			pt = cvPoint(-1,-1);

			IplImage* tmp = NULL;

			int xStart = 0, yStart = 0;
			int xEnd = s.Width, yEnd = s.Height;
			if(r<100) {
				xStart = yStart = p;
				xEnd = s.Width - p - g->width;
				yEnd = s.Height - p - g->height;
			}

			CvMat* histCalced = cvCreateMat(outputAlpha->height,outputAlpha->height,CV_8UC1);
			cvZero(histCalced);

			bool foundAnySpot = false;

			for(int i=xStart; i < xEnd; i+=scanStep) {
				for(int j=yStart; j < yEnd; j+=scanStep) {
					
					//skip points that already have textons on them
					int checkX = i+(g->width/2);
					checkX = (checkX>s.Width-1)?s.Width-1:checkX;
					int checkY = j+(g->height/2);
					checkY = (checkY>s.Height-1)?s.Height-1:checkY;
					if(!IsPointBlack(outputAlpha,checkX,checkY)) continue;

					foundAnySpot = true;

					//Inflate baloon in point to get "center" of weight
					CvPoint pnt = cvPoint(checkX,checkY);
					int a,b;
					InflateBaloonFromPoint(pnt,outputAlpha,a,b,s,props);

					if(cvGet2D(histCalced,pnt.x,pnt.y).val[0] > 0) {
						continue; //already calculated this histogram
					}

					cvSet2D(histCalced,pnt.x,pnt.y,cvScalar(255.0));

					if(props->Debug) {
						if(!tmp) { tmp = cvCreateImage(cvGetSize(outputAlpha),8,3);}
						//draw the to-be calculated rect
						cvCvtColor(outputClusters,tmp,CV_GRAY2RGB);
						cvRectangle(tmp,cvPoint((pnt.x-p < 0) ? 0 : pnt.x-p,(pnt.y-p < 0) ? 0 : pnt.y-p),
							cvPoint(min(pnt.x+g->width+p,s.Width-1),min(pnt.y+g->height+p,s.Height-1)),
							CV_RGB(255,0,0),2);
						cvShowImage("texture alpha",tmp);
						cvWaitKey(1);
					}

					//Compare this point surrounding hist to texton surrounding hist.
					double long score = 0;
					CvRect rect = cvRect((pnt.x-p < 0) ? 0 : pnt.x-p,(pnt.y-p < 0) ? 0 : pnt.y-p,	
						g->width+p*2,g->height+p*2);

					CvMat* vec = CalcHistogramForRect(outputClusters, rect, props->NumberOfClusters);
					for(int it=0;it<vec->cols;it++) {
						double ob = cvGet1D(vec,it).val[0];
						double e = cvGet1D(g->GetSurroundingHist(),it).val[0];
						//score += (long)(((o-e)*(o-e)) / e);	//chi square test
						score += (long)((ob-e)*(ob-e)); //sum of square diff
					}
					cvReleaseMat(&vec);

					score /= (rect.width * rect.height); //divide by window size
					//printf("%d,%d: %d\n",i,j,score);
					if(min > score) {
						pt = cvPoint(pnt.x,pnt.y);
						min = score;
					}
				}
			}
			cvReleaseMat(&histCalced);

			if(!foundAnySpot) scanStep /= 2;	//no spots left on grid, reduce scan step

			if(props->Debug) {
				cvWaitKey(1);
				printf("best score (%.3f) at point: %d,%d\n",min,pt.x,pt.y);
				if(tmp) {
					cvReleaseImage(&tmp);
				}
			}
		} else if(props->ModelType == TexSynthProps::eMODELTYPE_ENUM::MODELTYPE_USE_BALOON_TO_SELECT_TEXTON) {
			//select random point to put texton
			pt = cvPoint(rand() % s.Width,rand() % s.Height);
			if(!IsPointBlack(outputAlpha,pt.x,pt.y)) continue;

			int a=0,b=0;

			//start "inflating" baloon inside point
			InflateBaloonFromPoint(pt,outputAlpha,a,b,s,props);

			double baloonArea = MY_PI * a * b;

			int i = 0;
			for(;i<gs.size() && gs[i].area < baloonArea;i++);

			int randomizer = rand()%10;
			randomizer -= 5;
			i = i + randomizer;
			i = (i<=0)?1:i;
			i = (i>=gs.size())?gs.size()-1:i;

			g = g_clusters[gs[i].index];
		}
		int x = pt.x, y = pt.y;

		x -= cvRound(g->width / 2.0);
		y -= cvRound(g->height / 2.0);

		x = (x < 0) ? 0 : x;
		y = (y < 0) ? 0 : y;

		int fixX = 0,fixY = 0;
		if(x+g->width > s.Width) fixX = s.Width - (x + g->width);
		if(y+g->height > s.Height) fixY = s.Height - (y + g->height);

		int g_w = g->width + fixX;
		int g_h = g->height + fixY;

		cvSetImageROI(output,cvRect(x,y,g_w,g_h));
		cvSetImageROI(g->GetImage(),cvRect(0,0,g_w,g_h));
		cvSetImageROI(g->GetMask(),cvRect(0,0,g_w,g_h));
		cvSetImageROI(outputAlpha,cvRect(x,y,g_w,g_h));
		cvSetImageROI(outputClusters,cvRect(x,y,g_w,g_h));

		//prepare the mask to paint on final image (and final mask)
		IplImage* tmpMask = cvCreateImage(cvSize(g_w,g_h),8,1);
		cvSub(g->GetMask(),outputAlpha,tmpMask);
		cvCopy(g->GetMask(),outputAlpha,tmpMask);
		cvResetImageROI(g->GetMask());

		//paint the actual pixels on the final image
		cvCopy(g->GetImage(),output,tmpMask);

		//color the clusters map
		cvSet(outputClusters,cvScalar(g->levelFound/*cluster color*/),tmpMask);

		cvResetImageROI(g->GetImage());
		cvReleaseImage(&tmpMask);

		r++;

		cvResetImageROI(output);
		cvShowImage("texture",output);
		cvResetImageROI(outputAlpha);
		cvShowImage("texture alpha",outputAlpha);
		cvResetImageROI(outputClusters);

		CvScalar sm = cvSum(outputAlpha);
		if(sm.val[0] == allWhite) done = true;
	}
	delete[] used;

	cvWaitKey(0);

	cvDestroyWindow("texture");
	cvDestroyWindow("texture alpha");
	cvReleaseImage(&output);
	cvReleaseImage(&outputAlpha);
}

void InflateBaloonFromPoint(CvPoint& pt, IplImage* mask, int& a, int& b, System::Drawing::Size s, TexSynthProps^ props) {
	IplImage* tmpAlpha = NULL;
	bool done1 = false;
	a = 1;
	b = 1;
	CvPoint prevP, prevPrevP;
	prevP = cvPoint(-1,-1); prevPrevP = prevP;

	while(!done1) {
		done1 = !(CanBaloonGrowBySize(mask,pt,a,b,s));

		if(CanBaloonMoveBy(mask,pt,16,a,b,s)) {
			;//nop
		} else if(CanBaloonMoveBy(mask,pt,8,a,b,s)) {
			;//nop
		} else if(CanBaloonMoveBy(mask,pt,4,a,b,s)) {
			;//nop
		} else if(CanBaloonMoveBy(mask,pt,2,a,b,s)) {
			;//nop
		} else if(CanBaloonMoveBy(mask,pt,1,a,b,s)) {
			;
		} else {
			done1 = true;
		}
		
		if(!done1) {
			if(prevPrevP.x == pt.x && prevPrevP.y == pt.y) {
				done1 = true;
			}
		}

		prevPrevP = prevP;
		prevP = pt;

		if(props->Debug) {
			if(!tmpAlpha) {
				tmpAlpha = cvCreateImage(cvGetSize(mask),8,3);
			}
			cvCvtColor(mask,tmpAlpha,CV_GRAY2RGB);
			cvEllipse(tmpAlpha,pt,cvSize(a,b),0.0,0.0,360.0,CV_RGB(255,0,0),CV_FILLED);
			cvShowImage("texture alpha",tmpAlpha);

			char c = cvWaitKey(10);
			if (c == VK_ESCAPE) done1 = true;
		}
	}
	if(props->Debug) {
		cvWaitKey(0);
		if(tmpAlpha) {
			cvReleaseImage(&tmpAlpha);
		}
	}
}

bool CanBaloonGrowBySize(IplImage* mask, CvPoint pt, int& a, int& b, System::Drawing::Size s) {
	//double logSize = log((double)s.Width);
	//double floorLogSize = floor(logSize/LOG2);
	int growSize = 32; //(int)pow(2.0,floorLogSize);
	while(growSize >= 1) {
		if(CanBaloonGrowBy(mask,pt,growSize,a,b,s)) {
			return true;
		} else {
			growSize /= 2;
		}
	}
	return false;
}

bool CanBaloonGrowBy(IplImage* i,CvPoint pt, int by, int& a, int& b, System::Drawing::Size s) {
	if(CanBaloonGrow(i,pt,a+by,b+by,s.Width,s.Height)) {
		a+=by;
		b+=by;
	} else if(CanBaloonGrow(i,pt,a+by,b,s.Width,s.Height)) {
		a+=by;
	} else if(CanBaloonGrow(i,pt,a,b+by,s.Width,s.Height)) {
		b+=by;
	} else {
		return false;
	}
	return true;
}

bool CanBaloonMoveBy(IplImage* i,CvPoint& pt, int by, int a, int b, System::Drawing::Size s) {
	if(CanBaloonGrow(i,cvPoint(pt.x-by,pt.y),a,b,s.Width,s.Height)) {
		pt.x = pt.x - by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x+by,pt.y),a,b,s.Width,s.Height)) {
		pt.x = pt.x + by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x,pt.y-by),a,b,s.Width,s.Height)) {
		pt.y = pt.y - by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x,pt.y+by),a,b,s.Width,s.Height)) {
		pt.y = pt.y + by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x-by,pt.y-by),a,b,s.Width,s.Height)) {
		pt.x = pt.x - by;
		pt.y = pt.y - by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x+by,pt.y+by),a,b,s.Width,s.Height)) {
		pt.x = pt.x + by;
		pt.y = pt.y + by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x+by,pt.y-by),a,b,s.Width,s.Height)) {
		pt.y = pt.y - by;
		pt.x = pt.x + by;
	} else if(CanBaloonGrow(i,cvPoint(pt.x-by,pt.y+by),a,b,s.Width,s.Height)) {
		pt.y = pt.y + by;
		pt.x = pt.x - by;
	} else {
		return false;
	}
	return true;
}

bool IsPointBlack(IplImage* i,int x,int y) {
	unsigned char uc = (unsigned char)i->imageData[y*i->widthStep + x];
	if(uc == 255) //filled here, rand again
	{
		return false;
	}
	return true;
}

bool CanBaloonGrow(IplImage* im, CvPoint pt, int a, int b, int w, int h) {
	bool grow = true;
	float angle = 0.0;
	int how_many_points = (a+b)*(2);
	float angleStep = TWO_PI / (float)how_many_points;
	for(int i=0;i<how_many_points && grow;i++) {
		double cA = cos(angle), sA = sin(angle);
		double x = pt.x + cA * a, y = pt.y + sA * b;

		//cvCircle(tmpAlpha,cvPoint(pt.x + xP,pt.y + yP),1,CV_RGB(0,255,0),CV_FILLED);

		if(!(x < w && x > 0 &&
			y < h && y > 0 &&
			IsPointBlack(im,x,y))) {
			grow = false;
		}
		angle += angleStep;
	}
	return grow;
}

void CreateUniformTexture(System::Object^ o) {
	if(g_clusters.size() == 0) return;

	TextureSynthesizer::TexSynthProps^ props = (TextureSynthesizer::TexSynthProps^)o;

	System::Drawing::Size s = System::Drawing::Size(props->TextureWidth,props->TextureHeight);

	IplImage* output = cvCreateImage(cvSize(s.Width,s.Height),8,3);
	cvZero(output);
	IplImage* outputAlpha = cvCreateImage(cvGetSize(output),8,1);
	cvZero(outputAlpha);

	srand((int)(time(NULL)));

	cvNamedWindow("texture");
	cvNamedWindow("texture alpha");

	long allWhite = 255 * s.Width * s.Height;

	int* used = new int[g_clusters.size()];
	//int r = 0;
	bool done = false;
	while(!done) {
		//randomize location
		int x,y;
		x = rand()%(output->width);
		y = rand()%(output->height);

		unsigned char uc = (unsigned char)outputAlpha->imageData[y*outputAlpha->widthStep + x];
		if(uc == 255) //filled here, rand again
		{
			continue;
		}

		/*look for best texton to put here
		int bestTex = 0;
		int bestScore = INT_MAX;
		memset(used,0,g_clusters.size() * sizeof(int));
		for(int texIdx = 0; texIdx < g_clusters.size(); texIdx++) {
			TextonGroup* g = NULL;
			bool found = false;
			while(!found) {
				int randIdx = rand()%g_clusters.size();
				if(used[randIdx]) continue;
				g = g_clusters[randIdx];	//randomize, not just choose
				used[randIdx] = 1;
				found = true;
			}

			int fixX = 0,fixY = 0;
			if(x+g->width > s.Width) fixX = s.Width - (x + g->width);
			if(y+g->height > s.Height) fixY = s.Height - (y + g->height);

			int g_w = g->width + fixX;
			int g_h = g->height + fixY;

			CvMat* m = cvCreateMat(g_h,g_w,CV_32SC1);
			CvMat* tmp = cvCreateMat(g_h,g_w,CV_32SC1);
			cvSetImageROI(g->GetMask(),cvRect(0,0,g_w,g_h));
			cvConvert(g->GetMask(),m);
			cvResetImageROI(g->GetMask());
			cvSetImageROI(outputAlpha,cvRect(x,y,g_w,g_h));
			cvSet(tmp,cvScalar(255.0));
			cvConvertScale(outputAlpha,tmp,-1.0);
			cvSub(m,tmp,m);
			cvMul(m,m,m);
			CvScalar s = cvSum(m);
			if(bestScore > s.val[0]) {
				bestScore = s.val[0];
				bestTex = texIdx;
			}
			cvReleaseMat(&m);
			cvReleaseMat(&tmp);
		}
		*/
		int bestTex = rand()%g_clusters.size();

		TextonGroup* g = g_clusters[bestTex];

		//move left and up - half texton size
		x -= cvRound(g->width / 2.0);
		y -= cvRound(g->height / 2.0);

		x = (x < 0) ? 0 : x;
		y = (y < 0) ? 0 : y;

		//paint texton pixels
		int fixX = 0; 
		if(x+g->width > s.Width) 
			fixX = s.Width - (x + g->width);

		int fixY = 0;
		if(y+g->height > s.Height) 
			fixY = s.Height - (y + g->height);

		int g_w = g->width + fixX;
		int g_h = g->height + fixY;

		cvSetImageROI(g->GetImage(),cvRect(0,0,g_w,g_h));
		cvSetImageROI(g->GetMask(),cvRect(0,0,g_w,g_h));

		cvSetImageROI(output,cvRect(x,y,g_w,g_h));
		cvSetImageROI(outputAlpha,cvRect(x,y,g_w,g_h));

		//CvMat* tmpMask = cvCreateMat(g_h,g_w,CV_8UC1);
		IplImage* tmpMask = cvCreateImage(cvSize(g_w,g_h),8,1);

		////copy blend of texton and output
		//TMP = OUTPUT_A - MASK
		//cvSub(outputAlpha,g->GetMask(),tmpMask);
		//IplImage* tmp = cvCreateImage(cvSize(g_w,g_h),8,3);
		//cvAddWeighted(g->GetImage(),0.5,output,0.5,1.0,tmp);
		//cvCopy(output,tmp,tmpMask);
		//cvCopy(tmp,output);

		//add copied region to output mask
		//TMP = TEXTON_A - OUTPUT_A
		cvSub(g->GetMask(),outputAlpha,tmpMask);
		cvCopy(g->GetMask(),outputAlpha,tmpMask);
		cvResetImageROI(g->GetMask());

		//cvSmooth(tmpMask,tmpMask,2,7);

		//cvShowImage("texture alpha",tmpMask);
		//cvWaitKey(0);

		cvCopy(g->GetImage(),output,tmpMask);
		//TODO fix BlendCopy
		//BlendCopy(g->GetImage(),output,tmpMask);
		cvResetImageROI(g->GetImage());

		//cvReleaseMat(&tmpMask);
		cvReleaseImage(&tmpMask);

		//r++;

		cvResetImageROI(output);
		cvShowImage("texture",output);
		cvResetImageROI(outputAlpha);
		cvShowImage("texture alpha",outputAlpha);

		CvScalar sm = cvSum(outputAlpha);
		if(sm.val[0] == allWhite) done = true;


		int c;
		if(props->Debug) {
			c = cvWaitKey(0);
		} else {
			c = cvWaitKey(1);
		}
		if(c ==VK_ESCAPE) break;
	}
	delete[] used;

	cvWaitKey(0);

	cvDestroyWindow("texture");
	cvReleaseImage(&output);
	cvReleaseImage(&outputAlpha);
}

}//namespace
